/*
 * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package javax.swing;

import java.awt.AWTError;
import java.awt.LayoutManager;
import java.awt.Component;
import java.awt.Container;
import java.awt.Rectangle;
import java.awt.Point;
import java.awt.Dimension;
import java.awt.Insets;
import java.io.Serializable;

/**
 * The default layout manager for <code>JViewport</code>.
 * <code>ViewportLayout</code> defines
 * a policy for layout that should be useful for most applications.
 * The viewport makes its view the same size as the viewport,
 * however it will not make the view smaller than its minimum size.
 * As the viewport grows the view is kept bottom justified until
 * the entire view is visible, subsequently the view is kept top
 * justified.
 * <p>
 * <strong>Warning:</strong>
 * Serialized objects of this class will not be compatible with
 * future Swing releases. The current serialization support is
 * appropriate for short term storage or RMI between applications running
 * the same version of Swing.  As of 1.4, support for long term storage
 * of all JavaBeans&trade;
 * has been added to the <code>java.beans</code> package.
 * Please see {@link java.beans.XMLEncoder}.
 *
 * @author Hans Muller
 */
public class ViewportLayout implements LayoutManager, Serializable {

  // Single instance used by JViewport.
  static ViewportLayout SHARED_INSTANCE = new ViewportLayout();

  /**
   * Adds the specified component to the layout. Not used by this class.
   *
   * @param name the name of the component
   * @param c the the component to be added
   */
  public void addLayoutComponent(String name, Component c) {
  }

  /**
   * Removes the specified component from the layout. Not used by
   * this class.
   *
   * @param c the component to remove
   */
  public void removeLayoutComponent(Component c) {
  }


  /**
   * Returns the preferred dimensions for this layout given the components
   * in the specified target container.
   *
   * @param parent the component which needs to be laid out
   * @return a <code>Dimension</code> object containing the preferred dimensions
   * @see #minimumLayoutSize
   */
  public Dimension preferredLayoutSize(Container parent) {
    Component view = ((JViewport) parent).getView();
    if (view == null) {
      return new Dimension(0, 0);
    } else if (view instanceof Scrollable) {
      return ((Scrollable) view).getPreferredScrollableViewportSize();
    } else {
      return view.getPreferredSize();
    }
  }


  /**
   * Returns the minimum dimensions needed to layout the components
   * contained in the specified target container.
   *
   * @param parent the component which needs to be laid out
   * @return a <code>Dimension</code> object containing the minimum dimensions
   * @see #preferredLayoutSize
   */
  public Dimension minimumLayoutSize(Container parent) {
    return new Dimension(4, 4);
  }


  /**
   * Called by the AWT when the specified container needs to be laid out.
   *
   * @param parent the container to lay out
   * @throws AWTError if the target isn't the container specified to the <code>BoxLayout</code>
   * constructor
   */
  public void layoutContainer(Container parent) {
    JViewport vp = (JViewport) parent;
    Component view = vp.getView();
    Scrollable scrollableView = null;

    if (view == null) {
      return;
    } else if (view instanceof Scrollable) {
      scrollableView = (Scrollable) view;
    }

        /* All of the dimensions below are in view coordinates, except
         * vpSize which we're converting.
         */

    Insets insets = vp.getInsets();
    Dimension viewPrefSize = view.getPreferredSize();
    Dimension vpSize = vp.getSize();
    Dimension extentSize = vp.toViewCoordinates(vpSize);
    Dimension viewSize = new Dimension(viewPrefSize);

    if (scrollableView != null) {
      if (scrollableView.getScrollableTracksViewportWidth()) {
        viewSize.width = vpSize.width;
      }
      if (scrollableView.getScrollableTracksViewportHeight()) {
        viewSize.height = vpSize.height;
      }
    }

    Point viewPosition = vp.getViewPosition();

        /* If the new viewport size would leave empty space to the
         * right of the view, right justify the view or left justify
         * the view when the width of the view is smaller than the
         * container.
         */
    if (scrollableView == null ||
        vp.getParent() == null ||
        vp.getParent().getComponentOrientation().isLeftToRight()) {
      if ((viewPosition.x + extentSize.width) > viewSize.width) {
        viewPosition.x = Math.max(0, viewSize.width - extentSize.width);
      }
    } else {
      if (extentSize.width > viewSize.width) {
        viewPosition.x = viewSize.width - extentSize.width;
      } else {
        viewPosition.x = Math.max(0, Math.min(viewSize.width - extentSize.width, viewPosition.x));
      }
    }

        /* If the new viewport size would leave empty space below the
         * view, bottom justify the view or top justify the view when
         * the height of the view is smaller than the container.
         */
    if ((viewPosition.y + extentSize.height) > viewSize.height) {
      viewPosition.y = Math.max(0, viewSize.height - extentSize.height);
    }

        /* If we haven't been advised about how the viewports size
         * should change wrt to the viewport, i.e. if the view isn't
         * an instance of Scrollable, then adjust the views size as follows.
         *
         * If the origin of the view is showing and the viewport is
         * bigger than the views preferred size, then make the view
         * the same size as the viewport.
         */
    if (scrollableView == null) {
      if ((viewPosition.x == 0) && (vpSize.width > viewPrefSize.width)) {
        viewSize.width = vpSize.width;
      }
      if ((viewPosition.y == 0) && (vpSize.height > viewPrefSize.height)) {
        viewSize.height = vpSize.height;
      }
    }
    vp.setViewPosition(viewPosition);
    vp.setViewSize(viewSize);
  }
}
